feat: add a Data Consistency admin page (#1622) - #2086
Merged
Conversation
Extension.setActive() was routinely called as part of a full-entity merge/update. Hibernate generates a SQL UPDATE covering every mapped column, so a version-publish thread that read the entity before another thread flipped active could overwrite that flag back to its stale value once it saved - a classic lost update, not guarded by any transaction isolation level Postgres offers by default. Add @DynamicUpdate so Hibernate only includes changed columns in the UPDATE, closing the window for this specific race without introducing optimistic-locking retries/contention on a hot entity. Also add a native query to find extensions whose active flag disagrees with whether any of their versions is active, as a way to detect and repair rows that were already corrupted before this fix - used by the data-consistency check landing in the next commit.
Introduce a small, pluggable framework for detecting and repairing
known data inconsistencies, plus an admin UI for it:
- ConsistencyCheck: the extension point a check implements (id, name,
description, check(), fix(entityId)); autoFixOnSchedule() defaults
to true but lets a check opt out when a fix needs human judgment.
- ConsistencyCheckService: lists checks with their live finding count,
fetches findings for one check on demand, and fixes one or all
findings for a check. No persistence of past runs - findings are
always computed live against the current database state.
- ConsistencyCheckJobRequestHandler + ScheduleConsistencyCheckJobs: a
daily JobRunr job that runs every check and auto-fixes the ones that
allow it, so routine drift self-heals without an admin having to
notice and click a button.
- Every fix, whether run interactively by an admin or by the scheduled
job, is recorded via LogService under a system user
("ConsistencyCheckUser"), the same convention already used for
ExtensionControlService actions, so it shows up on the existing
Admin Logs page instead of a bespoke history view.
- ExtensionActiveFlagCheck: the first check, built on the
findExtensionsWithInconsistentActiveFlag() query added in the
previous commit, for extensions whose active flag disagrees with
whether any of their versions is active.
- ConsistencyAPI: GET /admin/consistency (summaries), GET
/admin/consistency/{checkId}/findings, POST
/admin/consistency/{checkId}/fix and .../fix/{entityId}.
- webui: a "Data Consistency" admin dashboard page showing one card per
check with its live finding count; expanding a card lazily loads its
findings with a "Fix" action per finding, and a "Fix all" action is
available directly on the card without expanding it. A "Refresh"
button re-fetches the check list on demand.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1622 and #1729 and #1733 .
What
Extension.activelost-update fix:@DynamicUpdateonExtensionsoHibernate only writes changed columns, closing a race where a full-entity
UPDATE from one thread could resurrect a stale
activeflag after anotherthread had legitimately flipped it. Also adds
findExtensionsWithInconsistentActiveFlag()to detect extensions alreadyleft in that state.
pluggable framework (
ConsistencyCheck) for this and future checks:ConsistencyCheckServicelists checks with a live finding count and canfix one finding or all findings for a check. Findings are always computed
live against the current DB state - nothing is persisted between runs.
need human judgment (
autoFixOnSchedule(), opt-out per check).LogServiceunder asystem user (
ConsistencyCheckUser), the same convention used byExtensionControlService, so it shows up on the existing Admin Logs page.ExtensionActiveFlagCheck./admin/consistency; a new "Data Consistency" pagein the admin dashboard with one card per check, a "Fix all" action
directly on the card, per-finding "Fix" once expanded, and a manual
"Refresh".
Testing
./gradlew test: 987 tests passing.yarn test(webui): 74 tests passing.yarn lint(webui): clean.🤖 Generated with Claude Code